/*
MIT License

Copyright (c) 2022 МГТУ им. Н.Э. Баумана, кафедра ИУ-6, Михаил Фетисов,

https://bmstu.codes/lsx/simodo/loom
*/

#include "simodo/variable/Variable.h"

#include "simodo/inout/convert/functions.h"

#include <cassert>

using namespace std;
using namespace simodo::variable;


u16string simodo::variable::toString(const Value & value, bool need_whole_info, bool quote_strings) 
{
    u16string str;

    switch(value.type())
    {
    case ValueType::String:
        if (!holds_alternative<u16string>(value.variant()))
            str = u"<"+inout::toU16(getValueTypeName(value.type()))+ u">";
        else {
            u16string text = get<u16string>(value.variant());

            if (quote_strings) {
                inout::replaceAll(text, u"\\", u"\\\\");
                inout::replaceAll(text, u"\"", u"\\\"");
                text = u"\""+text+u"\"";
            }
            str = text;
        }
        break;
    case ValueType::Bool:
        str = holds_alternative<bool>(value.variant())
                ? (get<bool>(value.variant()) ? u"true"s : u"false"s)
                : u"<"+inout::toU16(getValueTypeName(value.type()))+ u">";
        break;
    case ValueType::Int:
        str = holds_alternative<int64_t>(value.variant())
                ? inout::toU16(to_string(get<int64_t>(value.variant())))
                : u"<"+inout::toU16(getValueTypeName(value.type()))+ u">";
        break;
    case ValueType::Float:
        str = holds_alternative<double>(value.variant())
                ? inout::toU16(inout::clearNumberFractionalPart(to_string(get<double>(value.variant()))))
                : u"<"+inout::toU16(getValueTypeName(value.type()))+ u">";
        break;
    // case ValueType::Undefined:
    //     str = u"undef";
    //     break;
    // case ValueType::Function:
    //     if (name.substr(0,4) == u"get_") {
    //         const VariableSet_t & params = get<shared_ptr<Record>>(value.variant())->variables();
    //         assert(params.size() >= 2);
    //         if(params.size() == 2 && params[1].declared != DeclaredType::Error) {
    //             callFunction(var,{},InterpreterMode::FullExecution);
    //             return toString(_return_value, need_whole_info, true);
    //         }
    //     }
    //     [[fallthrough]];
    case ValueType::Object:
        if (!holds_alternative<shared_ptr<Object>>(value.variant()))
            str = u"<" + inout::toU16(getValueTypeName(value.type())) + u">";
        else {
            str   = u"{";
            const VariableSet_t & record = get<shared_ptr<Object>>(value.variant())->variables();
            bool                  first = true;

            for(const Variable & n : record) {
                const Variable & n_origin = n.origin();

                if (n_origin.type() == ValueType::Function && n.name().substr(0,4) == u"set_" && !need_whole_info)
                    continue;

                if (n_origin.type() == ValueType::Function && n.name().substr(0,4) != u"get_" && !need_whole_info)
                    continue;

                if (!first)
                    str += u", ";

                if (n.name().empty())
                    str += u"\"\":" + toString(n.origin().value(), need_whole_info);
                else if (n_origin.type() == ValueType::Function && n.name().substr(0,4) == u"get_")
                    str += n.name().substr(4) + u":" + toString(n.origin().value(), need_whole_info);
                else
                    str += n.name() + u":" + toString(n.origin().value(), need_whole_info, true);

                first = false;
            }
            str += u"}";
        }
        break;
    case ValueType::Array:
        if (!holds_alternative<shared_ptr<Array>>(value.variant()))
            str = u"<"+inout::toU16(getValueTypeName(value.type()))+ u">";
        else {
            str = u"[";
            const std::shared_ptr<Array> array = value.getArray();
            bool first = true;

            for(size_t i=0; i < array->values().size(); ++i)
            {
                if (!first) str += u", ";
                str += toString(array->values()[i], need_whole_info, true);
                first = false;
            }
            str += u"]";
        }
        break;
    default:
        {
            str = u"<" + inout::toU16(getValueTypeName(value.type()));

            if ((value.type() == ValueType::IntFunction && holds_alternative<InternalFunction>(value.variant()))
            || (value.type() == ValueType::ExtFunction && holds_alternative<ExternalFunction>(value.variant())))
                str += u"{}"; // Обозначаем непустую (определённую) функцию

            str+=u">";
        }
        break;
    }

    return str;
}
